package oop.ex3.crosswords;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;

import oop.ex3.crosswords.CrosswordStructure.SlotType;
import oop.ex3.search.SearchBoardNode;

public class MyCrossword implements Crossword{
	
	private CrosswordGlossary glossary;
	
	private CrosswordStructure structure;
	
	// the current board quality
	private int boardQuality=0;
	// the best quality that can be achieved from this board
	private int qualityUpperBound;
	// a list of moves 
	protected List<CrosswordEntry> movesRecord = new LinkedList<CrosswordEntry>();
	
	//a comparator for term list
	Comparator<String> treeCompare=new Comparator<String>(){

		@Override
		public int compare(String arg0, String arg1) {
			int resault=arg0.length()-arg1.length();
			if(resault==0)
				return arg0.compareToIgnoreCase(arg1);
			return resault;
		}
		
	};
	
	//list of terms ordered by size two words with same size ordered by name
	protected TreeSet<String> termList=new TreeSet<String>();
	
	//looks for a place vertically in the structure to insert a term 
	//and returns a crossword position to insert the term to
	private CrosswordPosition insertTermVertical(String term){
		//term can't be inserted vertically
		if(term.length()>structure.getHeight())
			return null;
		
		int startX=0;
		int startY=0;
		//go over rows
		for(int i=0;i<structure.getWidth();i++){
			int roomInRow=0;
			startX=i;
			startY=0;
			//go over slots in each row
			for(int j=0;j<structure.getHeight();j++){
				//if the term can be inserted return the start position to insert the term
				if(roomInRow==term.length()){
					return (new MyCrosswordPosition(startX, startY, true));
				}
				//while the slot type is unused slot increase counter
				if(structure.getSlotType(new MyCrosswordPosition(i, j, true))==SlotType.UNUSED_SLOT){
					roomInRow++;
				}
				//if slot type is frame slot update start position, reset counter
				else{
					roomInRow=0;
					startY=j+1;
				}
			}
		}
		
		return null;
	}
	
	//looks for a place horizontally in the structure to insert a term 
	//and returns a crossword position to insert the term to 
	private CrosswordPosition insertTermHorizontal(String term){			
		//term can't be inserted vertically
		if(term.length()>structure.getWidth())
			return null;
			
		int startX=0;
		int startY=0;
		//go over rows
		for(int i=0;i<structure.getHeight();i++){
			int roomInLine=0;
			startX=0;
			startY=i;
			//go over slots in each row
			for(int j=0;j<structure.getWidth();j++){
				//if the term can be inserted return the start position to insert the term
				if(roomInLine==term.length()){
					return (new MyCrosswordPosition(startX, startY, true));
				}
				//while the slot type is unused slot increase counter
				if(structure.getSlotType(new MyCrosswordPosition(i, j, true))==SlotType.UNUSED_SLOT){
					roomInLine++;
				}
				//if slot type is frame slot update start position, reset counter
				else{
					roomInLine=0;
					startX=j+1;
				}
			}
		}
			
		return null;
	}
	//return a linked list with optimized future moves
	//TODO check optimization
	private LinkedList<CrosswordEntry> getFutureMoves(){
		LinkedList<CrosswordEntry> futureMoves=new LinkedList<CrosswordEntry>();
		
		for(String s:termList){
			MyCrosswordPosition enteryPosition= (MyCrosswordPosition) insertTermVertical(s);
			if (enteryPosition==null){
				enteryPosition=(MyCrosswordPosition) insertTermHorizontal(s);
			}
			if(enteryPosition!=null){
				futureMoves.add(new MyCrosswordEntry(s, glossary.getTermDefinition(s),  enteryPosition));
			}
		}
		return futureMoves;
	}
	
	@Override
	public boolean isBestSolution() {
		
		return (boardQuality==qualityUpperBound);
	}

	@Override
	public Iterator<CrosswordEntry> getMovesIterator() {
		//Inefficient implementation
		
		return getFutureMoves().iterator();
	}

	@Override
	public int getQuality() {
		
		return boardQuality;
	}

	@Override
	public int getQualityBound() {
		int retVal=0;
		for(String s:glossary.getTerms()){
			retVal+=s.length();
		}
		return retVal;
	}

	@Override
	public void doMove(CrosswordEntry move) {
		// TODO Auto-generated method stub
		boardQuality+=move.getLength();
		//add a move to moves list
		this.movesRecord.add(0,move);
		this.termList.remove(move.getTerm());
	}

	@Override
	public void undoMove(CrosswordEntry move) {
		// TODO Auto-generated method stub
		boardQuality-=move.getLength();
		// removes a move from moves list
		movesRecord.remove(0);
		
		this.termList.add(move.getTerm());
	}

	@Override
	public SearchBoardNode<CrosswordEntry> getCopy() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void attachGlossary(CrosswordGlossary glossary) {
		this.glossary=glossary;
		
		for(String s:glossary.getTerms()){
			termList.add(s.toLowerCase());
		}
	}
	
	@Override
	public void attachStructure(CrosswordStructure structure) {
		this.structure=structure;
		
	}

	@Override
	public Collection<CrosswordEntry> getCrosswordEntries() {
		return movesRecord;
	}

}
